package de.fhin.mas.agents.manager;

import jade.core.behaviours.Behaviour;
import de.fhin.mas.MASEvents;
import de.fhin.mas.MASServices;
import de.fhin.mas.agents.MASBaseAgent;
import de.fhin.mas.agents.manager.behaviours.HandleObjectLost;
import de.fhin.mas.agents.manager.behaviours.HandleSystemStop;
import de.fhin.mas.agents.manager.behaviours.InitAbortAndGoto2;
import de.fhin.mas.agents.manager.behaviours.InitNewOrder;
import de.fhin.mas.agents.manager.behaviours.InitResume;
import de.fhin.mas.agents.manager.behaviours.InitSystemStop;
import de.fhin.mas.agents.manager.behaviours.ServeAbortAndGoto;
import de.fhin.mas.agents.manager.behaviours.ServeNewOrder;
import de.fhin.mas.agents.manager.behaviours.ServeResume;
import de.fhin.mas.agents.manager.behaviours.ServeSystemStop;
import de.fhin.mas.agents.manager.behaviours.HandleUserInfos;
import de.fhin.mas.agents.manager.gui.MASGui;
import de.fhin.mas.ontology.Location;
import de.fhin.mas.ontology.SearchableObject;

/**
 * Manager-Agent im MAS-System
 * <p>
 * Interagiert mit Benutzer. Benutzer kann darüber das System steuern.
 * 
 * @author Ast
 * @author Sbo
 * 
 */
public class ManagerAgent extends MASBaseAgent {
	private static final long serialVersionUID = 6401369506309749399L;

	/**
	 * Infos über den aktuell auszuführenden Auftrag.
	 */
	private Location currentStorageLocation;
	private SearchableObject currentSearchableObject;

	/**
	 * Aktuell ausgeführte Behaviours, die nicht doppelt im Agenten aktiv sein
	 * dürfen.
	 */
	private InitNewOrder activeNewOrderBv = null;
	private InitAbortAndGoto2 activeAbortAndGotoBv = null;

	/**
	 * Sonstiges.
	 */
	public MASGui myWindow;

	public ManagerAgent() {
		super(MASServices.MANAGER);

		// Starten der GUI
		myWindow = new MASGui(this);
		myWindow.setVisible(true);
	}

	/**
	 * Initial aktive Behaviours erzeugen + starten.
	 */
	@Override
	protected void addBehaviours() {
		// Damit die Tests möglich sind
		// werden Server-Behaviours benötigt...
		addBehaviour(new ServeNewOrder(this));
		addBehaviour(new ServeSystemStop(this));
		addBehaviour(new ServeResume(this));
		addBehaviour(new ServeAbortAndGoto(this));

		// Handler-Behaviours...
		addBehaviour(new HandleObjectLost(this));
		addBehaviour(new HandleSystemStop(this));

		// Das Behaviour, dass von anderen Agenten
		// ausgelöste Ereignisse in der GUI ausgibt starten...
		addBehaviour(new HandleUserInfos(this));
	}

	/**
	 * Subscriben zu den für den Manager-Agent nötigen Message-Topics.
	 */
	@Override
	protected void addTopicSubsciptions() {
		subscribeToTopic(MASEvents.NEW_ORDER);
		subscribeToTopic(MASEvents.CHANGE_OF_LOCATION_NECESSARY);
		subscribeToTopic(MASEvents.TARGET_FOUND);
		subscribeToTopic(MASEvents.TARGET_SEARCH_STARTED);
		subscribeToTopic(MASEvents.TARGET_SEARCH_CANCELED);
		subscribeToTopic(MASEvents.TARGET_SEARCH_PAUSED);
		subscribeToTopic(MASEvents.TARGET_SEARCH_RESUMED);
		subscribeToTopic(MASEvents.ENVIRONMENT_SCAN_STARTED);
		subscribeToTopic(MASEvents.ENVIRONMENT_SCAN_FINISHED);
		subscribeToTopic(MASEvents.ENVIRONMENT_SCAN_CANCELED);
		subscribeToTopic(MASEvents.TARGET_OBJECT_REACHED);
		subscribeToTopic(MASEvents.STORAGE_REACHED);
		subscribeToTopic(MASEvents.CHARGING_STATION_REACHED);
		subscribeToTopic(MASEvents.LOADING_OBJECT);
		subscribeToTopic(MASEvents.UNLOADING_OBJECT);
		subscribeToTopic(MASEvents.OBJECT_UNLOADED);
		subscribeToTopic(MASEvents.OBJECT_OUT_OF_REACH);
		subscribeToTopic(MASEvents.OBJECT_LOST);
		subscribeToTopic(MASEvents.OBJECT_UNSEIZABLE);
	}

	/**
	 * Neuen Auftrags anstoßen.
	 * 
	 * @param object
	 *            Zu transportierendes Objekt.
	 * @param target
	 *            Ziel, wo das Objekt abzuliefern ist.
	 */
	public void initNewOrder(SearchableObject object, Location target) {
		if (activeAbortAndGotoBv != null) {
			sendMsgToUser("AbortAndGoto get's initialized at the moment! Please wait!");
		} else if (activeNewOrderBv != null)
			sendMsgToUser("Another NewOrder-Behaviour is already active!");
		else {
			currentSearchableObject = object;
			currentStorageLocation = target;
			activeNewOrderBv = new InitNewOrder(this, object, target);
			addBehaviour(activeNewOrderBv);
		}
	}

	/**
	 * Fortfahren des pausierten MAS-Systems anstoßen.
	 */
	public void initResume() {
		addBehaviour(new InitResume(this));
	}

	/**
	 * Sofortigen Stop des MAS-Systems anstoßen.
	 * 
	 * @param reason
	 *            Grund für den Stop (wird an Benutzer weitergegeben).
	 */
	public void initSystemStop(Object reason) {
		addBehaviour(new InitSystemStop(this, reason));
	}

	/**
	 * Abbruch des aktuellen Auftrags und ansteuern eines neuen Ziels anstoßen.
	 * 
	 * @param newTarget
	 *            Neue anzusteuerndes Ziel.
	 */
	public void initAbortAndGoto(Location newTarget) {
		if (activeNewOrderBv != null) {
			sendMsgToUser("A new order is currently being initialized!");
		} else if (activeAbortAndGotoBv != null)
			sendMsgToUser("Another AbortAndGoto-Behaviour is already active!");
		else {
			// Aktueller Auftrag wird dadurch abgebrochen
			// --> Infos dazu zurücksetzen.
			currentStorageLocation = null;
			currentSearchableObject = null;

			activeAbortAndGotoBv = new InitAbortAndGoto2(this, newTarget);
			addBehaviour(activeAbortAndGotoBv);
		}
	}

	/**
	 * Neustart des aktuellen Auftrags.
	 */
	public void initRestartOrder() {
		if ((currentStorageLocation == null) || (currentStorageLocation == null))
			sendMsgToUser("The is no active order that could be restarted!");
		else
			initNewOrder(currentSearchableObject, currentStorageLocation);
	}

	/**
	 * Info an den Benutzer
	 * 
	 * @param msg
	 */
	public void sendMsgToUser(String msg) {
		myWindow.setMessage(msg);
	}

	/**
	 * Feststellen des Zustands der Behaviours.
	 */
	@Override
	public void notifyChangeBehaviourState(Behaviour b, String from, String to) {
		if (b.done()) {
			if (b == activeNewOrderBv) {
				// siehe "initNewOrder"
				removeBehaviour(activeNewOrderBv);
				activeNewOrderBv = null;
				logDebug("NEWORDERBV-DONE");
			} else if (b == activeAbortAndGotoBv) {
				// siehe "initAbortANdGoto"
				removeBehaviour(activeAbortAndGotoBv);
				activeAbortAndGotoBv = null;
				logDebug("ABORTANDGOTO-DONE");

			}
		}
		super.notifyChangeBehaviourState(b, from, to);
	}

}
